﻿using System;
using System.Runtime.InteropServices;
using HttpMonitorLib;
using System.Collections.Generic;
using CSharp.Libs.Misc;
using System.Collections;
using CSharp.Libs.Koc;
using CSharp.Libs.Net;
using System.Text.RegularExpressions;



namespace CSharp.Libs.Koc.KernelModels
{
    public class TrafficMonitor
    {
        #region declarations
        private Kernel _AB = null;
        private HttpMonClass _httpMon = null;

        // MainSrc and Seed specific
        private Dictionary<int, String> _dicMainSrc = new Dictionary<int, String>();
        private Int32 _intMainSrcId = 0;
        private Int32 _intSeedCount = 0;
        private String _strRecognizeStringMainSrcPhp = "";
        private String _strMainSrcSeedStart = "";
        private String _strMainSrcSeedEnd = "";
        
        // Seed update specific
        private String _strUpdateSeedJson = "";
        private Hashtable _htUpdateSeed = null;

        // fb parameter specific
        private Dictionary<String, String> _dicFbPostParameters = new Dictionary<String, String>();
        private String _strRecognizeStringFbSig = "";
        private String _strCurrentServerId = "";

        // fb userid specific
        private const String FBUID_SEEK = "Env={user:";
        private String _strUserID = "";
        private Boolean _fUserIsValid = false;

        #region dll imports
        [DllImport("user32.dll")]
        private static extern IntPtr GetTopWindow(IntPtr hWnd);
        #endregion

        #region events
        public delegate void SeedChangedEventHandler(object sender);
        public event SeedChangedEventHandler SeedChanged;
        private void ThrowSeedChangedEvent()
        {
            if(SeedChanged != null)
            {
                SeedChanged(this);
            }
        }

        public delegate void FbParametersChangedEventHandler(object sender);
        public event FbParametersChangedEventHandler FbParametersChanged;
        private void ThrowFbParametersChangedEvent()
        {
            if (FbParametersChanged != null)
            {
                FbParametersChanged(this);
            }
        }

        public delegate void KocServerChangedEventHandler(object sender);
        public event KocServerChangedEventHandler KocServerChanged;
        private void ThrowKocServerChangedEvent()
        {
            if(KocServerChanged != null)
            {
                KocServerChanged(this);
            }
        }

        public delegate void UserIsValidChangedEventHandler(object sender);
        public event UserIsValidChangedEventHandler UserIsValidChanged;
        private void ThrowUserIsValidChangedEvent()
        {
            if (UserIsValidChanged != null)
            {
                UserIsValidChanged(this);
            }
        }

        public delegate void UpdateSeedReceivedEventHandler(object sender, UpdateSeedReceivedEventArgs ea);
        public event UpdateSeedReceivedEventHandler UpdateSeedReceived;
        private void ThrowUpdateSeedReceivedEvent(
            Hashtable UpdateSeed)
        {
            if (UpdateSeedReceived != null)
            {
                UpdateSeedReceived(this,new UpdateSeedReceivedEventArgs(UpdateSeed));
            }
        }
        #endregion
        #endregion



        #region constructor / terminator
        public TrafficMonitor(
            Kernel AB)
        {
            _AB = AB;

            _strRecognizeStringMainSrcPhp = _AB.LAO.GetOption(constants.LAO_RECOGNIZE_STRING_PHP_MAIN_SRC);
            _strMainSrcSeedStart = _AB.LAO.GetOption(constants.LAO_SEEK_STRING_MAIN_SRC_SEED_START);
            _strMainSrcSeedEnd = _AB.LAO.GetOption(constants.LAO_SEEK_STRING_MAIN_SRC_SEED_END);
            _strRecognizeStringFbSig = _AB.LAO.GetOption(constants.LAO_RECOGNIZE_STRING_FB_SIG);
        }

        ~TrafficMonitor()
        {
            if (_httpMon != null)
            {
                //_httpMon.OnRequest -= _httpMon_OnRequest;
                //_httpMon.OnRedirect -= _httpMon_OnRedirect;
                //_httpMon.OnDataRecieved -= _httpMon_OnDataRecieved;
                //_httpMon.OnResponse -= _httpMon_OnResponse;

                _httpMon = null;
            }

            if (_AB != null)
            {
                _AB = null;
            }
        }
        #endregion



        #region event handling
        private void _httpMon_OnRequest(int id, string url, string headers, string method, object postData)
        {
            try
            {
                //Fetch FbParameters / check if still valid.
                if (url.Contains(URLHolder.PHP_GET_CHAT))
                {
                    String strPostData = ByteArrayToString((Byte[])postData);

                    if (!_AB.UH.URLGetChat.URLMaybeValid)
                    {
                        _AB.UH.ParseURLGetChat(String.Concat(url, "?", strPostData));
                        _strCurrentServerId = _AB.UH.URLGetChat.Parameters[URLHolder.PARAM_SID];//25.03.2011 Added ServerId

                        ThrowFbParametersChangedEvent();
                        ThrowKocServerChangedEvent();
                    }
                    else
                    {
                        //Check if FbParameters changed
                        Dictionary<String, String> dicNewParams = _AB.UH.GetParameterDictionary(strPostData);
                        Boolean fParamsInvalid = false;

                        //25.03.2011 Added ServerId
                        if (_strCurrentServerId != dicNewParams[URLHolder.PARAM_SID])
                        {
                            _strCurrentServerId = dicNewParams[URLHolder.PARAM_SID];
                            ThrowKocServerChangedEvent();
                        }

                        //24.03.2011 Check if UserID is still valid
                        if (dicNewParams[URLHolder.PARAM_FB_SIG_USER] != _strUserID)
                        {
                            _strUserID = dicNewParams[URLHolder.PARAM_FB_SIG_USER];

                            if (_AB.UV.UserIsValid(_strUserID))
                            {
                                _fUserIsValid = true;
                                ThrowUserIsValidChangedEvent();
                            }
                            else
                            {
                                _fUserIsValid = false;
                                _strUserID = "";
                                ThrowFbParametersChangedEvent();
                                ThrowUserIsValidChangedEvent();
                            }
                        }

                        //Compare fb sig parameters
                        foreach (KeyValuePair<String, String> KVP in _AB.UH.URLGetChat.Parameters)
                        {
                            if (KVP.Key.Contains(_strRecognizeStringFbSig))
                            {
                                if (!dicNewParams.ContainsKey(KVP.Key))
                                {
                                    fParamsInvalid = true;
                                    break;
                                }
                                else
                                {
                                    if (dicNewParams[KVP.Key] != KVP.Value)
                                    {
                                        fParamsInvalid = true;
                                        break;
                                    }
                                }
                            }
                        }

                        //if FbParameters changed, parse new.
                        if (fParamsInvalid)
                        {
                            _AB.UH.ParseURLGetChat(String.Concat(url, "?", strPostData));
                            ThrowFbParametersChangedEvent();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _AB.EC.AddException(ex, this.ToString(), _AB.EC.Tools.GetCurrentMethodName());
            }
        }

        private void _httpMon_OnRedirect(int id, int redirectedId, string url, string redirectedUrl, string responseHeaders, string requestHeaders)
        {
        }

        private void _httpMon_OnDataRecieved(int id, string url, object data, int status)
        {
            //Get data as string
            String strData = ByteArrayToString((Byte[])data);

            //24.03.2011 Check if UserID is valid
            if (strData.Contains(FBUID_SEEK))
            {
                _strUserID = _GetFbUserId(strData);
                _fUserIsValid = _AB.UV.UserIsValid(_strUserID);
                ThrowFbParametersChangedEvent();
                ThrowUserIsValidChangedEvent();
            }

            //Receive seed
            if(url.ToLower().Contains(_strRecognizeStringMainSrcPhp))
            {
                if(!_dicMainSrc.ContainsKey(id))
                {
                    _dicMainSrc.Clear();
                }

                if(_dicMainSrc.Count == 0)
                {
                    _intMainSrcId = id;
                    _dicMainSrc.Add(_intMainSrcId, "");
                }

                _dicMainSrc[_intMainSrcId] = String.Concat(_dicMainSrc[_intMainSrcId], strData);

                //status 1 seems to say that webpage is done loading.
                if (status == 1)
                {
                    _GetSeed();
                }
            }

            //Reaction on UpdateSeed
            if (url.Contains(URLHolder.PHP_UPDATE_SEED))
            {
                _strUpdateSeedJson = String.Concat(_strUpdateSeedJson, strData);

                if (status == 1)
                {
                    if (_ReceivedSeedUpdate())
                    {
                        ThrowUpdateSeedReceivedEvent(_htUpdateSeed);
                        _strUpdateSeedJson = ""; //Clear String to prepare for next UpdateSeed
                    }
                }
            }
        }

        private void _httpMon_OnResponse(int id, string url, int responseCode, string headers)
        {
        }
        #endregion



        #region private functions
        #region initialisations
        /// <summary>_InitMonitor
        ///     initializes monitor class
        /// </summary>
        /// <param name="WebBrowserHandle">Handle of WebBrowser Control</param>
        private void _InitMonitor(
            IntPtr WebBrowserHandle)
        {
            if(_httpMon == null)
            {
                _httpMon = new HttpMonitorLib.HttpMonClass();
                _httpMon.IEWindow = GetTopWindow(GetTopWindow(WebBrowserHandle)).ToInt32();
                _httpMon.OnRequest += new HttpMonitorLib._IHttpMonEvents_OnRequestEventHandler(_httpMon_OnRequest);
                _httpMon.OnRedirect += new HttpMonitorLib._IHttpMonEvents_OnRedirectEventHandler(_httpMon_OnRedirect);
                _httpMon.OnDataRecieved += new HttpMonitorLib._IHttpMonEvents_OnDataRecievedEventHandler(_httpMon_OnDataRecieved);
                _httpMon.OnResponse += new HttpMonitorLib._IHttpMonEvents_OnResponseEventHandler(_httpMon_OnResponse);
            }
        }

        /// <summary>_DeinitMonitor
        ///     releases monitor class
        /// </summary>
        private void _DeinitMonitor()
        {
            if(_httpMon != null)
            {
                _httpMon.OnRequest -= _httpMon_OnRequest;
                _httpMon.OnRedirect -= _httpMon_OnRedirect;
                _httpMon.OnDataRecieved -= _httpMon_OnDataRecieved;
                _httpMon.OnResponse -= _httpMon_OnResponse;

                _httpMon = null;
            }
        }
        #endregion

        #region seed receiving
        /// <summary>_GetSeed
        ///     retreives the seed variable off the Main_Src page.
        /// </summary>
        /// <history>created 21.03.2011</history>
        private void _GetSeed()
        {
            try
            {
                //see if a MainSrc page is loaded
                if (_intMainSrcId != 0)
                {
                    //get starting index of seed variabe in page's source
                    Int32 intStart = _dicMainSrc[_intMainSrcId].IndexOf(_strMainSrcSeedStart) + _strMainSrcSeedStart.Length + 1;

                    if(intStart > 0)
                    {
                        //get ending index of seed variable in page's source
                        Int32 intEnd = _dicMainSrc[_intMainSrcId].IndexOf(_strMainSrcSeedEnd, intStart + 1);

                        if(intEnd > intStart)
                        {
                            //get seed variable's string
                            String strSeed = _dicMainSrc[_intMainSrcId].Substring(intStart, intEnd - intStart);
                            
                            if(strSeed != "")
                            {
                                //decode seed JSON
                                _AB.BSVM.JsonSeed = JSON.JsonDecode(strSeed);

                                //increment seed counter and throw seed event
                                _intSeedCount++;
                                ThrowSeedChangedEvent();
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>_GetFbUserId
        /// 
        /// </summary>
        /// <param name="strSeek"></param>
        /// <returns></returns>
        private String _GetFbUserId(
            String strSeek)
        {
            try
            {
                //get starting index of uid
                Int32 intStart = strSeek.IndexOf(FBUID_SEEK) + FBUID_SEEK.Length;

                if (intStart > 0)
                {
                    //get ending index of seed variable in page's source
                    Int32 intEnd = strSeek.IndexOf(",", intStart);

                    if (intEnd > intStart)
                    {
                        //get seed variable's string
                        return strSeek.Substring(intStart, intEnd - intStart);
                    }
                }
            }
            catch (Exception ex)
            {
                _AB.EC.AddException(ex, this.ToString(), _AB.EC.Tools.GetCurrentMethodName());
            }

            return "";
        }
        #endregion

        #region seed update receiving
        /// <summary>_ReceivedSeedUpdate</summary>
        /// <returns></returns>
        /// <history>10.05.2011</history>
        private Boolean _ReceivedSeedUpdate()
        {
            try
            {
                _htUpdateSeed = JSON.JsonDecode(_strUpdateSeedJson) as Hashtable;

                return _AB.ReturnHashtableOk(_htUpdateSeed);

                //if (_htUpdateSeed != null)
                //{
                //    if (_htUpdateSeed[constants.KEY_OK] is Boolean)
                //    {
                //        return (Boolean)_htUpdateSeed[constants.KEY_OK];
                //    }
                //    else if (_htUpdateSeed[constants.KEY_OK] is String)
                //    {
                //        return Boolean.Parse(_htUpdateSeed[constants.KEY_OK].ToString());
                //    }
                //}
            }
            catch (Exception ex)
            {
                _AB.EC.AddException(ex, this.ToString(), _AB.EC.Tools.GetCurrentMethodName());
            }

            return false;
        }
        #endregion

        #region common functions
        private byte[] StringToByteArray(string str)
        {
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            return enc.GetBytes(str);
        }

        private string ByteArrayToString(byte[] arr)
        {
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            return enc.GetString(arr);
        }
        #endregion
        #endregion



        #region interface
        #region properties
        #region seed
        public Int32 SeedCount
        {
            get
            {
                return _intSeedCount;
            }
        }

        public Hashtable UpdateSeed
        {
            get
            {
                return _htUpdateSeed;
            }
        }
        #endregion

        #region FB parameters
        public Dictionary<String, String> fbPostParameters
        {
            get
            {
                return _dicFbPostParameters;
            }
        }

        public String FBUserID
        {
            get
            {
                return _strUserID;
            }
        }

        public Boolean UserIsValid
        {
            get
            {
                return _fUserIsValid;
            }
        }

        public String CurrentServerId
        {
            get
            {
                return _strCurrentServerId;
            }
        }
        #endregion
        #endregion

        #region methods
        public void InitMonitor(
            IntPtr WebBrowserHandle)
        {
            _InitMonitor(WebBrowserHandle);
        }

        public void DeinitMonitor()
        {
            _DeinitMonitor();
        }
        #endregion
        #endregion
    }
}
